#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# This program is free software; you can redistribute it and/or modify  
# it under the terms of the GNU General Public License as published by  
# the Free Software Foundation; either version 2 of the License, or     
# (at your option) any later version.                                   
#                                                                         
# A copy of the license can be found in the license.txt file supplied   
# with this software or at: http://www.gnu.org/copyleft/gpl.html       
#

#
# Cabinet made up files to hold tons of data
#


import os,os.path,fnmatch
from icewood import tinylog

class   DataCell(object) : pass

class   FileCell(object) :
    SWITCH_THRESHOLD = 1024 * 1024 * 1024

    WRITE_FILENAME_PREFIX = 'data'

    FILENO_STARTUP = '0001'

    def __init__(self, path) :
        '''
        @param path path to hold data
        '''
        self._path = path

        self._write_fileno = self.FILENO_STARTUP
        self._write_filename = None
        self._write_fp = None

        self._read_fileno = None
        self._read_filename = None
        self._read_fp = None

        self._switch_threshold = self.SWITCH_THRESHOLD

        self._logger = tinylog.getLogger( self.__class__.__name__ )
        ## __init__()

    def __del__(self) :
        self.close()
        ## __del__()

    def close(self) :
        if self._write_fp :
            self._write_fp.close()
            self._write_fp = None

        if self._read_fp :
            self._read_fp.close()
            self._read_fp = None
        ## close()


    def getLocation(self) :
        '''get location to write new data

        location ::= fileno + offset
        '''
        self.__prewrite()

        offset = self._write_fp.tell()

        return (self._write_fileno, offset)
        ## getLocation()

    def write(self, data, location=None) :
        '''write data to file cabinet; 
        
        @param data data to write
        @param location to verify that it is the location to write data to
        '''
        self.__prewrite()

        self._write_fp.write(data)
        self._write_fp.write('\n')
        ## write()

    def __prewrite(self) :
        if not self._write_fp :
            self._write_fileno = self.__getNextWriteFileNo()
            self._write_filename = '%s%s%s.%s' % (self._path, os.sep, self.WRITE_FILENAME_PREFIX, self._write_fileno)
            self._write_fp = open(self._write_filename, 'a', 1)

        # test if need switch
        offset = self._write_fp.tell()
        while offset >= self._switch_threshold :
            self.__switchWrite()
            offset = self._write_fp.tell()
        ## __prewrite()

    def __getNextWriteFileNo(self) :
        next_fileno = self.FILENO_STARTUP
        WRITE_FILENAME_PATTERN = '%s.*' % self.WRITE_FILENAME_PREFIX
        filenames = os.listdir(self._path) 
        for fn in filenames :
            if fnmatch.fnmatch(fn, WRITE_FILENAME_PATTERN) :
                x = fn.split('.')
                if x[-1] > next_fileno :
                    next_fileno = x[-1]
        
        return next_fileno
        ## __getNextWriteFileNo()

    def __switchWrite(self) :
        self._write_fp.close()

        fileno_int = int(self._write_fileno, 16)
        self._write_fileno = '%04x' % (fileno_int + 1)
        self._write_filename = '%s%s%s.%s' % (self._path, os.sep, self.WRITE_FILENAME_PREFIX, self._write_fileno)
        self._write_fp = open(self._write_filename, 'a', 1)
        ## __switchWrite()

    ###################################################################


    def read(self, location=None) :
        '''read data from file cabitnet; if location is present, read from the right location
        '''
        if location :
            fileno,offset = location
        else :
            fileno,offset = None,None
        self.__preread( fileno )

        if offset is not None :
            self._read_fp.seek( offset )

        data = self._read_fp.readline()
        if not data :
            # empty data, means EOF
            ok = self.__trySwitchRead()
            if not ok :
                return ''
            data = self._read_fp.readline()
            # try to switch to next file

        return data.strip()
        ## read()

    def __preread(self, fileno) :
        if fileno is None :
            if self._read_fp :
                return
            else :
                fileno = self.FILENO_STARTUP

        if fileno != self._read_fileno :
            if self._read_fp :
                self._read_fp.close()
                self._read_fp = None

            self._read_fileno = fileno
            self._read_filename = self.__getReadFileName( fileno )
            self._read_fp = open(self._read_filename, 'r')
        ## __preread()

    def __getReadFileName(self, fileno) :

        filename = '%s%s%s.%s' % (self._path, os.sep, self.WRITE_FILENAME_PREFIX, fileno)

        return filename
        ## __getReadFileName()

    def __trySwitchRead(self) :
        fileno = self.__getNextReadFileNo()
        read_filename = self.__getReadFileName( fileno )
        if os.path.exists( read_filename ) :
            if self._read_fp :
                self._read_fp.close()

            self._read_fileno = fileno
            self._read_filename = read_filename
            self._read_fp = open(self._read_filename, 'r')

            return True

        return False
        ## __trySwitchRead()

    def __getNextReadFileNo(self) :
        fileno_int = int(self._read_fileno, 16)
        read_fileno = '%04x' % (fileno_int + 1)

        return read_fileno
        ## __getNextReadFileNo()


    ###################################################################

    def setSwitchThreshold(self, size_to_switch) :
        if size_to_switch > 0 :
            self._switch_threshold = size_to_switch
        ## setSwitchThreshold()

    ## class FileCell


